using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using DirkRettschlag.Utils;
using System.Data;
using WSSoapCompression;
using System.Diagnostics;

[WebService(Namespace = "http://rettschlag.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class DepartureWebService : System.Web.Services.WebService
{
    #region Variables

    HttpWebRequest request;
    HttpWebResponse response;
    System.IO.StreamReader sr;
    string source;
    List<string> deviceIDs;

    #endregion Variables

    public class Input
    {
        private string stationField;

        private string stationIDField;

        private string transfersField;

        private string transfersSelectedField;

        private bool useTrainsFilterField;

        private string trainsFilterField;

        private bool useDirectionsFilterField;

        private string directionsFilterField;

        private bool usePlatformsFilterField;

        private string platformsFilterField;

        private bool useAndFilterField;

        /// <remarks/>
        public string Station
        {
            get
            {
                return this.stationField;
            }
            set
            {
                this.stationField = value;
            }
        }

        /// <remarks/>
        public string StationID
        {
            get
            {
                return this.stationIDField;
            }
            set
            {
                this.stationIDField = value;
            }
        }

        /// <remarks/>
        public string Transfers
        {
            get
            {
                return this.transfersField;
            }
            set
            {
                this.transfersField = value;
            }
        }

        /// <remarks/>
        public string TransfersSelected
        {
            get
            {
                return this.transfersSelectedField;
            }
            set
            {
                this.transfersSelectedField = value;
            }
        }

        /// <remarks/>
        public bool UseTrainsFilter
        {
            get
            {
                return this.useTrainsFilterField;
            }
            set
            {
                this.useTrainsFilterField = value;
            }
        }

        /// <remarks/>
        public string TrainsFilter
        {
            get
            {
                return this.trainsFilterField;
            }
            set
            {
                this.trainsFilterField = value;
            }
        }

        /// <remarks/>
        public bool UseDirectionsFilter
        {
            get
            {
                return this.useDirectionsFilterField;
            }
            set
            {
                this.useDirectionsFilterField = value;
            }
        }

        /// <remarks/>
        public string DirectionsFilter
        {
            get
            {
                return this.directionsFilterField;
            }
            set
            {
                this.directionsFilterField = value;
            }
        }

        /// <remarks/>
        public bool UsePlatformsFilter
        {
            get
            {
                return this.usePlatformsFilterField;
            }
            set
            {
                this.usePlatformsFilterField = value;
            }
        }

        /// <remarks/>
        public string PlatformsFilter
        {
            get
            {
                return this.platformsFilterField;
            }
            set
            {
                this.platformsFilterField = value;
            }
        }

        /// <remarks/>
        public bool UseAndFilter
        {
            get
            {
                return this.useAndFilterField;
            }
            set
            {
                this.useAndFilterField = value;
            }
        }
    }
                
    #region Constructor

    public DepartureWebService()
    {
        //Uncomment the following line if using designed components 
        //InitializeComponent(); 

        deviceIDs = new List<string>();

        deviceIDs.Add("3FBF5000-7351-0801-1706-8AB2475903EB");
        deviceIDs.Add("00197735-0302-0115-0028-0050BF3F5173");
        deviceIDs.Add("3FBF5000-7351-0801-5735-570123585090");
        deviceIDs.Add("3FBF5000-7351-0801-5735-570101275260");
        deviceIDs.Add("3FBF5000-7351-0801-7535-590112889600");
        deviceIDs.Add("3FBF5000-7351-0801-7535-590112932590");

    }

    #endregion Constructor

    #region WebMethods

    [WebMethod(Description = "Get filtered departures", EnableSession = false)]
    [WSCompression(CompressionLevels.High)]
    public string GetDepartures(string DeviceID, Input input)
    {
        return getDepartures(DeviceID, input);
    }

    [WebMethod(Description = "Find stations matching the given input.", EnableSession = false)]
    [WSCompression(CompressionLevels.High)]
    public List<string> FindStation(string DeviceID, string Name)
    {
        return findStation(DeviceID, Name);
    }

    #endregion WebMethods

    #region Private Methods

    private List<string> findStation(string DeviceID, string Name)
    {
        if (!deviceIDs.Contains(DeviceID))
        {
            throw new Exception("You are not allowed to use this Webservice");
        }

        List<string> stations = new List<string>();

        request = (HttpWebRequest)WebRequest.Create("http://mobile.bahn.de/bin/mobil/bhftafel.exe/en?input=" + Name + "&time=actual&boardType=dep&productsFilter=0000001000000000&start=Suchen&boardType=Abfahrt&ao=yes");

        request.Credentials = CredentialCache.DefaultCredentials;
        request.KeepAlive = true;
        request.Method = "GET";

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();

        StreamReader sr = new StreamReader(response.GetResponseStream());

        string source = sr.ReadToEnd();

        sr.Close();

        if (source.IndexOf("Your input is ambiguous.") >= 0)
        {
            Regex regex = new Regex("<select class=\"error\" id=\"rplc0\".*?</select>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            Match match;
            MatchCollection matchcollection;

            match = regex.Match(source);

            if (match.Success)
            {

                regex = new Regex("<option.*?>.*?</option>", RegexOptions.IgnoreCase);

                matchcollection = regex.Matches(match.Value);

                string station;

                foreach (Match tmpmatch in matchcollection)
                {
                    station = Regex.Replace(Utilities.ClearText(tmpmatch.Value), "<option value=\"", "");
                    station = Regex.Replace(station, "\".*", "");
                    stations.Add(station);
                }

            }
        }
        else
        {
            Regex regex = new Regex("<h1 class=\"pagetitle\"><a.*?input=(?<Name>.*?)\\&.*?</h1>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
            Match match;
            match = regex.Match(source);
            if (match.Success)
            {
                stations.Add(match.Groups["Name"].Value.Replace("%23","#"));
            }

        }

        return stations;
    }

    private string getDepartures(string DeviceID, Input input)
    {
        if (!deviceIDs.Contains(DeviceID))
        {
            throw new Exception("You are not allowed to use this Webservice");
        }
        string Timings = DateTime.UtcNow.ToString();
        string source = getSource(input);
        Timings = Timings + "|" + DateTime.UtcNow.ToString();
        string xml = getDepartureXML(source, input);
        Timings = Timings + "|" + DateTime.UtcNow.ToString();
        return xml + "<!-- " + Timings + " -->";
    }

    private string getSource(Input input)
    {
        string URL = "";
        if (input.StationID == "")
            URL = "http://mobile.bahn.de/bin/mobil/bhftafel.exe/en?input=" + input.Station + "&time=actual&boardType=dep" + input.Transfers + "&start=Suchen&ao=yes";
        else
            URL = "http://mobile.bahn.de/bin/mobil/bhftafel.exe/en?input=" + input.StationID + "&time=actual&boardType=dep" + input.Transfers + "&start=Suchen&ao=yes";

        request = (HttpWebRequest)WebRequest.Create(URL);

        request.Credentials = CredentialCache.DefaultCredentials;
        request.KeepAlive = true;
        request.Method = "GET";

        response = (HttpWebResponse)request.GetResponse();

        sr = new System.IO.StreamReader(response.GetResponseStream());

        source = sr.ReadToEnd();

        sr.Close();
        return source;
    }

    private string getDepartureXML(string source, Input input)
    {
        DataTable dt = new DataTable("Departure");

        dt.Columns.Add("Time", typeof(string));
        dt.Columns.Add("Train", typeof(string));
        dt.Columns.Add("Icon", typeof(string));
        dt.Columns.Add("Direction", typeof(string));
        dt.Columns.Add("Platform", typeof(string));
        dt.Columns.Add("Later", typeof(string));
        dt.Columns.Add("Full_DateTime", typeof(string));
    
        try
        {

            string Time;
            string Train;
            string Icon;
            string Direction;
            string Platform;
            string Later;
            string FullDateTime;

            int currentItem = 0;
            int allItems = 0;

            if (this.source.IndexOf("Current departure time in") >= 0)
            {
                Regex regexCountColumns = new Regex("<th class=\"(time|icon|train|route|platform|ris).*?>.+?</th>", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);

                int countColumns = regexCountColumns.Matches(this.source).Count;

                // <tr>.*?<td class=\"time\".*?>(?<Time>.*?)</td>.*?<td class=\"train\".*?>.*?<img src=\"(?<Icon>.*?)_24x24.gif.*?<td class=\"train\".*?>.*?<a href.*?>(?<Train>.*?)</a>.*?<td class=\"route\".*?>.*?<a.*?href.*?>(?<Direction>.*?)</a>.*?</td>.*?<td class=\"platform\".*?>(?<Platform>.*?)<br />.*?</td>.*?<td class=\"ris\".*?>(?<Later>.*?)</td>

                Regex regex;
                if (countColumns == 4)
                {
                    regex = new Regex("<tr>.*?<td class=\"time\".*?>(?<Time>.*?)</td>.*?<td class=\"train\".*?>.*?<img src=\"(?<Icon>.*?)_24x24.gif.*?<td class=\"train\".*?>.*?<a href.*?>(?<Train>.*?)</a>.*?<td class=\"route\".*?>.*?<a.*?href.*?>(?<Direction>.*?)</a>.*?</td>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
                }
                else if (countColumns == 5)
                {
                    regex = new Regex("<tr>.*?<td class=\"time\".*?>(?<Time>.*?)</td>.*?<td class=\"train\".*?>.*?<img src=\"(?<Icon>.*?)_24x24.gif.*?<td class=\"train\".*?>.*?<a href.*?>(?<Train>.*?)</a>.*?<td class=\"route\".*?>.*?<a.*?href.*?>(?<Direction>.*?)</a>.*?</td>.*?<td class=\"platform\".*?>(?<Platform>.*?)<br />.*?</td>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
                }
                else if (countColumns == 6)
                {
                    regex = new Regex("<tr>.*?<td class=\"time\".*?>(?<Time>.*?)</td>.*?<td class=\"train\".*?>.*?<img src=\"(?<Icon>.*?)_24x24.gif.*?<td class=\"train\".*?>.*?<a href.*?>(?<Train>.*?)</a>.*?<td class=\"route\".*?>.*?<a.*?href.*?>(?<Direction>.*?)</a>.*?</td>.*?<td class=\"platform\".*?>(?<Platform>.*?)<br />.*?</td>.*?<td class=\"ris\".*?>(?<Later>.*?)</td>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
                }
                else
                {
                    throw new Exception("Source not valid.");
                }

                this.source = Regex.Replace(this.source, "^.*?<tr class=\"browse\">", "", RegexOptions.Singleline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
                this.source = Regex.Replace(this.source, "<tr class=\"browse\">.*", "", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);

                Debug.WriteLine(DateTime.Now.ToString());
                MatchCollection matchcollection = regex.Matches(this.source);

                Debug.WriteLine(DateTime.Now.ToString());
                //allItems = Regex.Matches(this.source, "<tr.*?>.*?</tr>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled).Count;

                if (matchcollection != null)
                    allItems = matchcollection.Count;

                if (allItems > 0)
                {

                    foreach (Match tmpmatch in matchcollection)
                    {
                        currentItem++;

                        Time = ClearTime(Utilities.ClearText(tmpmatch.Groups["Time"].Value)).Trim();
                        Train = ClearTrain(Utilities.ClearText(tmpmatch.Groups["Train"].Value)).Trim();
                        Icon = ClearIcon(tmpmatch.Groups["Icon"].Value);
                        Direction = Utilities.ClearText(tmpmatch.Groups["Direction"].Value).Trim();

                        if (tmpmatch.Groups.Count >= 4)
                            Platform = ClearPlatform(Utilities.ClearText(tmpmatch.Groups["Platform"].Value)).Trim();
                        else
                            Platform = "";
                        if (tmpmatch.Groups.Count >= 5)
                            Later = ClearLater(Utilities.ClearText(tmpmatch.Groups["Later"].Value)).Trim();
                        else
                            Later = "";

                        if (FilterData(Train, Direction, Platform, input))
                        {
                            DateTime FullDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, int.Parse(Time.Split(':')[0]), int.Parse(Time.Split(':')[1]), 0);

                            if (FullDate < DateTime.Now.AddMinutes(-2))
                                FullDate = FullDate.AddDays(1);

                            FullDateTime = FullDate.ToFileTimeUtc().ToString();

                            DataRow dr = dt.NewRow();
                            dr["Time"] = Time;
                            dr["Train"] = Train;
                            dr["Icon"] = Icon;
                            dr["Direction"] = Direction;
                            dr["Platform"] = Platform;
                            dr["Later"] = Later;
                            dr["Full_DateTime"] = FullDateTime;

                            dt.Rows.Add(dr);
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }

        StringWriter xmlOut = new StringWriter();
        dt.WriteXml(xmlOut);
        return xmlOut.ToString();

    }

    private bool FilterData(string Train, string Direction, string Platform, Input input)
    {
        Regex regex;

        bool filterhitTrain = false;
        bool filterhitDirection = false;
        bool filterhitPlatform = false;

        //// Bus Filter
        //regex = new Regex("^Bus", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        //if (input.TransfersSelected[7].ToString() == "0" && regex.Match(Train).Success)
        //    return false;
        //// IRE Filter
        //regex = new Regex("^IRE", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        //if (input.TransfersSelected[3].ToString() == "0" && regex.Match(Train).Success)
        //    return false;
        //// RE Filter
        //regex = new Regex("^RE", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        //if (input.TransfersSelected[4].ToString() == "0" && regex.Match(Train).Success)
        //    return false;
        //// RB Filter
        //regex = new Regex("^RB", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
        //if (input.TransfersSelected[5].ToString() == "0" && regex.Match(Train).Success)
        //    return false;

        // Train Filter
        if (input.UseTrainsFilter)
        {
            List<string> filterTrains = new List<string>(input.TrainsFilter.Split(';'));

            foreach (string filterTrain in filterTrains)
            {
                if (Train.ToLower() == filterTrain.ToLower())
                {
                    filterhitTrain = true;
                }
            }

            //regex = new Regex(this.stationSettings.TrainsFilter.Replace(";", "|"), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
            //if (regex.Match(Train).Success)
            //{
            //    filterhitTrain = true;
            //}
        }

        // Direction Filter
        if (input.UseDirectionsFilter)
        {
            regex = new Regex(input.DirectionsFilter.Replace(";", "|"), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
            if (regex.Match(Direction).Success)
            {
                filterhitDirection = true;
            }
        }

        // Platform Filter
        if (input.UsePlatformsFilter)
        {
            List<string> filterPlatforms = new List<string>(input.PlatformsFilter.Split(';'));

            foreach (string filterPlatform in filterPlatforms)
            {
                if (Platform.ToLower() == filterPlatform.ToLower())
                {
                    filterhitPlatform = true;
                }
            }

            //regex = new Regex(this.stationSettings.PlatformsFilter.Replace(";", "|"), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
            //if (regex.Match(Platform).Success)
            //{
            //    filterhitPlatform = true;
            //}
        }

        if (input.UseAndFilter)
        {
            if (input.UseTrainsFilter == true && input.UseDirectionsFilter == false && input.UsePlatformsFilter == false)
                return filterhitTrain;
            else if (input.UseTrainsFilter == true && input.UseDirectionsFilter == true && input.UsePlatformsFilter == false)
                return (filterhitTrain && filterhitDirection);
            else if (input.UseTrainsFilter == true && input.UseDirectionsFilter == true && input.UsePlatformsFilter == true)
                return (filterhitTrain && filterhitDirection && filterhitPlatform);
            else if (input.UseTrainsFilter == true && input.UseDirectionsFilter == false && input.UsePlatformsFilter == true)
                return (filterhitTrain && filterhitPlatform);
            else if (input.UseTrainsFilter == false && input.UseDirectionsFilter == false && input.UsePlatformsFilter == true)
                return (filterhitPlatform);
            else //if (input.useTrainsFilter == false && input.useDirectionsFilter == true && input.usePlatformsFilter == false)
                return (filterhitDirection);
        }
        else
            if (input.UseTrainsFilter == true || input.UseDirectionsFilter == true || input.UsePlatformsFilter == true)
                return (filterhitTrain || filterhitDirection || filterhitPlatform);
            else
                return true;
    }

    private string ClearLater(string input)
    {
        input = Regex.Replace(input, "<.*?>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);

        input = input.Replace("on time", "");
        input = input.Replace("-", "");
        input = input.Replace("n.a.", "");

        if (input.Length > 1)
        {
            Regex regex = new Regex(".*?(?<Later>approx..*?minute.*?later)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
            Match match = regex.Match(input);
            if (match.Groups.Count > 0)
                input = match.Groups["Later"].Value;
        }

        return input;
    }

    private string ClearPlatform(string input)
    {
        input = Regex.Replace(input, "<.*?>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);

        input = input.Replace("Platf.", "");

        return input;
    }

    private string ClearTime(string input)
    {
        input = input.Replace(" ", "");

        return input;
    }

    private string ClearDirection(string input)
    {
        input = input.Replace("<", "");
        input = input.Replace(">", "");

        return input;
    }

    private string ClearTrain(string input)
    {
        input = input.Replace("S     ", "");
        input = input.Replace("S    ", "");

        input = input.Replace("U     ", "");
        input = input.Replace("U    ", "");

        input = input.Replace(" ", "");

        return input;
    }

    private string ClearIcon(string input)
    {
        input = input.Remove(0, input.LastIndexOf("/") + 1);

        return input;
    }

    #endregion Private Methods

}
